package venus

import (
	"bufio"
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net/url"

	// "log"

	// "log"

	// "log"
	// "log"
	"net/http"
	"regexp"
	"strings"

	log "github.com/sirupsen/logrus"
)

var (
	HostParse      = regexp.MustCompile(`Host: .+`)
	HostAddrScheme = "https"
)

// func proxy(raw string) http.Request {

// 	for k, v := range req.Header {
// 		for _, value := range v {
// 			reqNew.Header.Add(k, value)
// 		}
// 	}

// 	if client := session.getClient(proxy...); client != nil {
// 		if session.RandomeUA {
// 			ix := rand.Int() % len(UA)
// 			ua := UA[ix]
// 			reqNew.Header.Set("User-agent", ua)
// 			// req.Header.Set("User-agent", ua)
// 		}

// 		res, err := client.Do(reqNew)
// 		// res, err := client.Do(req)
// 		if err != nil {
// 			return nil, err
// 		}
// 		resp = &SmartResponse{
// 			*res,
// 			res.StatusCode,
// 			reqNew.URL.String(),
// 			// req.URL.String(),
// 			nil,
// 		}

// 	} else {
// 		err = fmt.Errorf("proxy create error: %v", proxy)
// 	}
// 	return

// }

func RawToNewReq(raw string, newhost string) (reqNew *http.Request, copyReq *http.Request, err error) {

	var reader *bufio.Reader
	// var host string
	firstLine := strings.SplitN(raw, "\n", 2)[0]
	oldPath := strings.SplitN(firstLine, " ", 3)[1]
	fromLocal := false
	useTls := false
	var oldUrl *url.URL
	if strings.Contains(oldPath, FORWARDING_KEY) {
		u := PackUrlDe(oldPath)
		if strings.HasPrefix(string(u), "https") {
			useTls = true
		}
		urFields := strings.SplitN(string(u), "/", 4)

		host := urFields[2]
		if len(urFields) < 4 {
			// log.Info("fields:", string(u))
			log.Warn("[outer] ", u)
			raw = strings.Replace(raw, oldPath, "/", 1)
		} else {
			if host != "" && host != "localhost" && host != newhost {
				newhost = host
				// log.Info(" outer site request :", u)
			}
			ur := urFields[3]
			raw = strings.Replace(raw, oldPath, "/"+ur, 1)
		}

	} else {
		fromLocal = true
	}
	lines := strings.Split(raw, "\n")
	if useTls {
		oldUrl, _ = url.Parse(fmt.Sprintf("https://%s%s", newhost, strings.TrimSpace(strings.SplitN(lines[0], " ", 3)[1])))
	} else {
		oldUrl, _ = url.Parse(fmt.Sprintf("http://%s%s", newhost, strings.TrimSpace(strings.SplitN(lines[0], " ", 3)[1])))

	}
	hostLineNo := 1

	for i, l := range lines {
		if strings.Contains(l, "Host:") {
			hostLineNo = i
			break
		}
	}
	lines[1], lines[hostLineNo] = lines[hostLineNo], lines[1]
	raw = strings.TrimSpace(strings.Join(lines, "\n")) + "\n\n"

	if !strings.Contains(raw, "\r\n") {
		if strings.Contains(raw, "\n\n") {
			heeader := strings.SplitN(raw, "\n\n", 2)
			heeader[0] = strings.ReplaceAll(heeader[0], "\n", "\r\n")
			reader = bufio.NewReader(strings.NewReader((heeader[0] + "\r\n\r\n" + heeader[1])))
		} else {
			reader = bufio.NewReader(strings.NewReader(strings.ReplaceAll(strings.TrimSpace(raw)+"\n\n", "\n", "\r\n")))
			copyReq, _ = http.ReadRequest(bufio.NewReader(strings.NewReader(strings.ReplaceAll(strings.TrimSpace(raw)+"\n\n", "\n", "\r\n"))))

		}
	} else {
		reader = bufio.NewReader(strings.NewReader(raw))
		copyReq, _ = http.ReadRequest(bufio.NewReader(strings.NewReader(raw)))
	}
	req, err := http.ReadRequest(reader)
	if err != nil {
		return
	}
	req.Host = newhost

	if ref := req.Header.Get("Referer"); strings.Contains(ref, FORWARDING_KEY) {
		/*
			间接 重定型
			情形1： Reference:  xxxx/all_link_refer_to=xxxxxxx
		*/
		s := PackUrlDe(ref)
		if fromLocal {
			// baseu, _ := url.Parse(s)
			// base := path.Dir(baseu.Path)
			// if !strings.HasPrefix(oldUrl.Path, base) {
			// 	new := base + oldUrl.Path
			// 	log.Info("refer :", base, "\n\tnew: ", new)
			// 	oldUrl.Path = new
			// }

		}
		req.Header.Set("Referer", s)
	} else if strings.HasPrefix(ref, DEFAULT_SCHEME+"://"+HostAddr) {
		/*
			直接 重定向
			情形2： Reference:   http://localhsot/xxxxxxx
		*/
		req.Header.Set("Referer", strings.ReplaceAll(ref, HostAddr, newhost))
	} else if strings.HasPrefix(ref, DEFAULT_SCHEME+"://"+Domain) {
		/*
			情形3： 包含域名的重定向
		*/
		req.Header.Set("Referer", strings.ReplaceAll(ref, Domain, newhost))
	}

	if newhost != "" {
		// u, _ := url.Parse(fmt.Sprintf("http://%s%s", newhost, req.URL.Path))
		req.URL = oldUrl
		// lines[hostLineNo] = strings.SplitN(lines[hostLineNo], ":", 2)[0] + ": " + newhost
	}

	reqNew, err = http.NewRequest(req.Method, oldUrl.String(), req.Body)
	if err != nil {
		log.Fatal("new req errs:", err)
		return
	}

	reqNew.Host = newhost
	reqNew.URL = oldUrl
	reqNew.Header["Host"] = []string{newhost}

	if queryStr := oldUrl.Query().Encode(); queryStr != "" {
		reqNew.URL.RawQuery = queryStr
	}
	for k, vs := range req.Header {
		for _, v := range vs {
			reqNew.Header.Add(k, v)
		}
	}

	// if strings.Contains(raw, "fontawesome-webfont.woff") {
	// 	log.Info("raw:\n", raw)
	// 	log.Info("url:", oldUrl)
	// 	log.Info("new:\n", ReqString(reqNew))
	// }
	return
}

func CopyReq(req *http.Request) (clone *http.Request, clone2 *http.Request, err error) {
	var body []byte

	reqS := ReqString(req)
	tls := false
	if req.URL.Scheme == "https" {
		tls = true
	}
	body, err = ioutil.ReadAll(req.Body)
	if len(body) > 0 {
		fmt.Println(reqS + string(body))
	}
	clone, err = RawToReq(reqS+string(body), tls)
	clone2, err = RawToReq(reqS+string(body), tls)
	return
}

func RawToReq(raw string, usetls bool) (req *http.Request, err error) {
	lines := strings.Split(raw, "\n")
	f1s := strings.Fields(lines[0])
	Method := f1s[0]
	path := f1s[1]
	host := ""
	// req, err := http.ReadRequest
	// body := ""
	r := bytes.NewReader([]byte(raw))
	rawreq, _ := http.ReadRequest(bufio.NewReader(r))
	header := map[string][]string{}
	// if strings.Contains(raw, "\n\n") {
	// 	body = strings.SplitN(raw, "\n\n", 2)[1]
	// } else if strings.Contains(raw, "\r\n\r\n") {
	// 	body = strings.SplitN(raw, "\r\n\r\n", 2)[1]
	// } else {
	// 	return nil, errors.New("valid req header lack \r\n\r\n")
	// }
	for _, l := range lines[1:] {
		if strings.Contains(l, ":") {
			fs := strings.SplitN(l, ":", 2)
			header[fs[0]] = strings.Fields(strings.TrimSpace(fs[1]))
			if strings.Contains(l, "Host:") {
				host = strings.Fields(strings.TrimSpace(l))[1]
			}
		} else {
			break
		}

	}
	tlsH := ""
	if usetls {
		tlsH = "s"
	}
	// fmt.Println("body:", body)
	url := fmt.Sprintf("http%s://%s%s", tlsH, host, path)
	req, err = http.NewRequest(Method, url, rawreq.Body)
	req.Header = header
	return
}

func PackUrl(urlstr string) string {
	// log.WithField("test1", urlstr).Info("test")
	d := HostAddr
	if Domain != "" {
		d = Domain
	}
	u, _ := url.Parse(fmt.Sprintf("%s://%s", HostAddrScheme, d))
	vs := url.Values{}
	w := base64.StdEncoding.EncodeToString([]byte(urlstr))
	w = strings.ReplaceAll(w, "=", "_")
	// log.WithField("b64", w).Info()
	vs.Add(FORWARDING_KEY, w)
	u.Path = vs.Encode()
	return u.String()
}

func PackUrlDe(u string) string {
	if strings.Contains(u, FORWARDING_KEY) {
		value := strings.SplitN(strings.TrimSpace(u), "=", 2)[1]
		// value := strings.SplitN(oldPath, "=", 2)[1]
		value = strings.ReplaceAll(value, "_", "=")
		value = strings.ReplaceAll(value, "%25", "%")
		one, _ := url.QueryUnescape(value)
		u, _ := base64.StdEncoding.DecodeString(one)
		u = bytes.ReplaceAll(u, []byte("\\/"), []byte("/"))
		return string(u)
	} else {
		return u
	}
}

func UnPackUrl(urlstr string) (*url.URL, error) {
	if strings.Contains(urlstr, FORWARDING_KEY) {
		u, _ := url.Parse(urlstr)
		r := u.Query().Get(FORWARDING_KEY)
		r = strings.ReplaceAll(r, "_", "=")
		buf, _ := base64.StdEncoding.DecodeString(r)
		return url.Parse(string(buf))
	} else {
		return &url.URL{
			Path: urlstr,
		}, nil
	}

}
